package com.im.service.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.im.service.common.ImConstant;
import com.im.service.common.Result;
import com.im.service.domain.dto.ImChatDTO;
import com.im.service.domain.dto.UnreadMsg;
import com.im.service.domain.entity.ImChat;
import com.im.service.domain.entity.ImGroup;
import com.im.service.domain.entity.ImGroupUser;
import com.im.service.domain.entity.ImUser;
import com.im.service.domain.query.ChatHistoryQuery;
import com.im.service.domain.vo.ChatHistoryVO;
import com.im.service.domain.vo.ChatVO;
import com.im.service.mapper.ImChatMapper;
import com.im.service.mapper.ImGroupMapper;
import com.im.service.mapper.ImGroupUserMapper;
import com.im.service.mapper.ImUserMapper;
import com.im.service.service.ImChatService;
import org.jim.core.ImChannelContext;
import org.jim.core.ImPacket;
import org.jim.core.ImStatus;
import org.jim.core.packets.ChatBody;
import org.jim.core.packets.ChatType;
import org.jim.core.packets.Command;
import org.jim.core.packets.RespBody;
import org.jim.server.JimServerAPI;
import org.jim.server.util.ChatKit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 聊天列表 服务实现类
 * </p>
 *
 * @author 刘志强
 * @since 2024-07-04
 */
@Service
public class ImChatServiceImpl extends ServiceImpl<ImChatMapper, ImChat> implements ImChatService {

    public static final String userChat = "userChat";
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ImUserMapper imUserMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ImGroupUserMapper groupUserMapper;
    @Autowired
    private ImChatMapper chatMapper;
    @Autowired
    private ImGroupMapper groupMapper;

    @Override
    public Result<List<ChatVO>> myChat() {
        Long userId = StpUtil.getLoginIdAsLong();
        List<ChatVO> voList = chatMapper.myChat(userId);
        if (CollectionUtil.isEmpty(voList)) {
            return Result.okData(ListUtil.empty());
        }
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        List<String> imChatList = hashOperations.values(userChat + StrPool.COLON + userId);
        if (CollectionUtil.isNotEmpty(imChatList)) {
            Map<Long, ImChat> chatMap = new HashMap<>(imChatList.size());
            for (String json : imChatList) {
                ImChat imChat = JSONUtil.toBean(json, ImChat.class);
                chatMap.put(imChat.getTargetId(), imChat);
            }
            for (ChatVO chatVO : voList) {
                ImChat imChat = chatMap.get(chatVO.getTargetId());
                if (ObjUtil.isNotNull(imChat)) {
                    chatVO.setLastTime(imChat.getLastTime());
                    chatVO.setUnreadQuantity(imChat.getUnreadQuantity());
                    chatVO.setLastMessage(imChat.getLastMessage());
                }
            }
        }
        voList = voList.stream()
                .sorted(Comparator.comparing(ChatVO::getLastTime).reversed())
                .sorted(Comparator.comparing(ChatVO::getTopping).reversed())
                .collect(Collectors.toList());
        return Result.okData(voList);
    }

    @Override
    public Result<ChatHistoryVO> historyByTargetId(@RequestBody ChatHistoryQuery query) {
        List<String> historyList = new ArrayList<>();
        Long currentUserId = StpUtil.getLoginIdAsLong();
        switch (query.getType()) {
            case 1: {
                Long anotherUserId = query.getTargetId();
                historyList = getUserToUserHistory(currentUserId, anotherUserId, query);
            }
            break;
            case 2:
                Long groupId = query.getTargetId();
                historyList = getGroupHistory(groupId, query);
                break;
            default:
                query.setStartIndex(0L);
                break;
        }
        ChatHistoryVO chatHistoryVO = new ChatHistoryVO();
        chatHistoryVO.setMessageList(historyList);
        chatHistoryVO.setEndIndex(query.getStartIndex());
        return Result.okData(chatHistoryVO);
    }

    private List<String> getGroupHistory(Long groupId, ChatHistoryQuery query) {
        String key = "store:group:" + groupId;
        Boolean exists = stringRedisTemplate.hasKey(key);
        if (exists) {
            if (StrUtil.isNotBlank(query.getKeyword())) {
                Set<String> set = stringRedisTemplate.opsForZSet().range(key, 0, Long.MAX_VALUE);
                return set.stream().filter(msg -> {
                    JSONObject object = JSONUtil.parseObj(msg);
                    if (StrUtil.contains(object.getStr("content"), query.getKeyword())) {
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());
            } else {
                if (ObjUtil.isNull(query.getStartIndex()) || ObjUtil.isNull(query.getEndIndex())) {
                    Long count = stringRedisTemplate.opsForZSet().zCard(key);
                    query.setEndIndex(count);
                    if (count > 50L) {
                        query.setStartIndex(count - 50L);
                    } else {
                        query.setStartIndex(0L);
                    }
                }
                Set<String> set = stringRedisTemplate.opsForZSet().range(key, query.getStartIndex(), query.getEndIndex());
                return ListUtil.toList(set);
            }
        }
        query.setStartIndex(0L);
        return new ArrayList<>();

    }

    @Override
    public Result<ImChat> createChat(ImChatDTO chatDTO) {
        // 查询会话是否存在
        List<ImChat> chatList = chatMapper.selectList(
                Wrappers.lambdaQuery(ImChat.class)
                        .eq(ImChat::getUserId, chatDTO.getUserId())
                        .eq(ImChat::getTargetId, chatDTO.getTargetUserId())
        );
        if (CollectionUtil.isNotEmpty(chatList)) {
            return Result.okData(chatList.get(0));
        }
        // 创建会话
        ImChat imChat = new ImChat();
        imChat.setUserId(chatDTO.getUserId());
        imChat.setType(chatDTO.getType());
        imChat.setTargetId(chatDTO.getTargetUserId());
        imChat.setLastMessage(chatDTO.getLastMessage());
        imChat.setLastTime(LocalDateTime.now());
        imChat.setUnreadQuantity(0);
        if (StrUtil.isNotBlank(chatDTO.getLastMessage())) {
            imChat.setUnreadQuantity(1);
        }
        imChat.setDelFlag(0);
        imChat.setTopping(0);
        if (ObjUtil.equal(imChat.getTargetId(), ImConstant.SysUserId)) {
            imChat.setTopping(1);
        }
        this.save(imChat);
        // 缓存创建
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        hashOperations.put(userChat + StrPool.COLON + imChat.getUserId(), String.valueOf(imChat.getTargetId()), JSONUtil.toJsonStr(imChat));
        // 发送通知 会话创建
        ImPacket imPacket = new ImPacket();
        imPacket.setStatus(ImStatus.C10000);
        imPacket.setCommand(Command.NEW_CHAT);
        ChatVO chatVO = toVO(imChat);
        imPacket.setBody(new RespBody(Command.NEW_CHAT, chatVO).toByte());
        JimServerAPI.sendToUser(String.valueOf(imChat.getUserId()), imPacket);
        return Result.okData(imChat);
    }

    @Override
    public void removeChat(Long id) {
        ImChat imChat = this.getById(id);
        this.removeById(id);
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(userChat + StrPool.COLON + imChat.getUserId(), String.valueOf(imChat.getTargetId()));
        // 发送通知 会话移除
        ImPacket imPacket = new ImPacket();
        imPacket.setStatus(ImStatus.C10000);
        imPacket.setCommand(Command.REMOVE_CHAT);
        ChatVO chatVO = toVO(imChat);
        imPacket.setBody(new RespBody(Command.REMOVE_CHAT, chatVO).toByte());
        JimServerAPI.sendToUser(String.valueOf(imChat.getUserId()), imPacket);
    }

    @Override
    public void chatProcessing(ChatBody chatBody, ImChannelContext imChannelContext) {
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        if (ObjUtil.equal(ChatType.CHAT_TYPE_PRIVATE.getNumber(), chatBody.getChatType())) {
            // 私聊

            // toChat
            String toChatJson = hashOperations.get(userChat + StrPool.COLON + chatBody.getTo(), String.valueOf(chatBody.getFrom()));
            ImChat toChat;
            if (StrUtil.isBlank(toChatJson)) {
                ImChatDTO chatDTO = new ImChatDTO();
                chatDTO.setUserId(Long.parseLong(chatBody.getTo()));
                chatDTO.setTargetUserId(Long.parseLong(chatBody.getFrom()));
                chatDTO.setType(1);
                chatDTO.setLastMessage(chatBody.getContent());
                Result<ImChat> result = this.createChat(chatDTO);
                toChat = result.getData();
            } else {
                toChat = JSONUtil.toBean(toChatJson, ImChat.class);
                List<Long> chatIds = getChatKeep(toChat.getUserId());
                if (chatIds.contains(toChat.getTargetId())) {
                    toChat.setUnreadQuantity(0);
                } else {
                    toChat.setUnreadQuantity(toChat.getUnreadQuantity() + 1);
                }
                toChat.setLastMessage(chatBody.getContent());
                toChat.setLastTime(LocalDateTime.now());
            }
            hashOperations.put(userChat + StrPool.COLON + chatBody.getTo(), String.valueOf(chatBody.getFrom()), JSONUtil.toJsonStr(toChat));
            // fromChat
            String fromChatJson = hashOperations.get(userChat + StrPool.COLON + chatBody.getFrom(), String.valueOf(chatBody.getTo()));
            ImChat fromChat;
            if (StrUtil.isBlank(fromChatJson)) {
                ImChatDTO chatDTO = new ImChatDTO();
                chatDTO.setUserId(Long.parseLong(chatBody.getFrom()));
                chatDTO.setTargetUserId(Long.parseLong(chatBody.getTo()));
                chatDTO.setType(1);
                chatDTO.setLastMessage(chatBody.getContent());
                Result<ImChat> result = this.createChat(chatDTO);
                fromChat = result.getData();
            } else {
                fromChat = JSONUtil.toBean(fromChatJson, ImChat.class);
            }
            fromChat.setLastMessage(chatBody.getContent());
            fromChat.setLastTime(LocalDateTime.now());
            fromChat.setUnreadQuantity(0);

            List<Long> chatIds = getChatKeep(toChat.getUserId());
            if (chatIds.contains(toChat.getTargetId())) {
                fromChat.setUnreadMsgIds("[]");
            } else {
                String unreadMsgIds = "[]";
                if (StrUtil.isNotBlank(fromChat.getUnreadMsgIds())) {
                    unreadMsgIds = fromChat.getUnreadMsgIds();
                }
                List<String> unreadMsgIdList = JSONUtil.toList(unreadMsgIds, String.class);
                unreadMsgIdList.add(chatBody.getId());
                fromChat.setUnreadMsgIds(JSONUtil.toJsonStr(unreadMsgIdList));

                // 通知 发送者 未读会话
                if (ObjUtil.isNotNull(imChannelContext)) {
                    ImPacket imPacket = new ImPacket();
                    imPacket.setStatus(ImStatus.C10000);
                    imPacket.setCommand(Command.UNREAD_MSG_IDS);
                    UnreadMsg unreadMsg = new UnreadMsg(fromChat.getTargetId(), unreadMsgIdList);
                    imPacket.setBody(new RespBody(Command.UNREAD_MSG_IDS, unreadMsg).toByte());
                    JimServerAPI.send(imChannelContext, imPacket);
                }
            }

            hashOperations.put(userChat + StrPool.COLON + chatBody.getFrom(), String.valueOf(chatBody.getTo()), JSONUtil.toJsonStr(fromChat));


        } else if (ObjUtil.equal(ChatType.CHAT_TYPE_PUBLIC.getNumber(), chatBody.getChatType())) {
            // 群聊
            List<ImGroupUser> users = groupUserMapper.selectList(
                    Wrappers.lambdaQuery(ImGroupUser.class)
                            .eq(ImGroupUser::getGroupId, chatBody.getGroupId())
            );
            for (ImGroupUser groupUser : users) {
                String groupChatJson = hashOperations.get(userChat + StrPool.COLON + groupUser.getUserId(), String.valueOf(groupUser.getGroupId()));
                ImChat groupChat;
                if (StrUtil.isBlank(groupChatJson)) {
                    ImChatDTO chatDTO = new ImChatDTO();
                    chatDTO.setUserId(groupUser.getUserId());
                    chatDTO.setTargetUserId(groupUser.getGroupId());
                    chatDTO.setType(2);
                    chatDTO.setLastMessage(chatBody.getContent());
                    Result<ImChat> result = this.createChat(chatDTO);
                    groupChat = result.getData();
                } else {
                    groupChat = JSONUtil.toBean(groupChatJson, ImChat.class);
                    List<Long> chatIds = getChatKeep(groupChat.getUserId());
                    groupChat.setLastMessage(chatBody.getContent());
                    groupChat.setLastTime(LocalDateTime.now());
                    if (chatIds.contains(groupChat.getTargetId())) {
                        groupChat.setUnreadQuantity(0);
                    } else {
                        groupChat.setUnreadQuantity(groupChat.getUnreadQuantity() + 1);
                    }
                }
                hashOperations.put(userChat + StrPool.COLON + groupUser.getUserId(), String.valueOf(groupUser.getGroupId()), JSONUtil.toJsonStr(groupChat));
            }
        }
    }


    private List<String> getUserToUserHistory(Long userId1, Long userId2, ChatHistoryQuery query) {
        String key = "store:user:" + ChatKit.sessionId(String.valueOf(userId1), String.valueOf(userId2));
        Boolean exists = stringRedisTemplate.hasKey(key);
        if (exists) {
            if (StrUtil.isNotBlank(query.getKeyword())) {
                Set<String> set = stringRedisTemplate.opsForZSet().range(key, 0, Long.MAX_VALUE);
                return set.stream().filter(msg -> {
                    JSONObject object = JSONUtil.parseObj(msg);
                    if (StrUtil.contains(object.getStr("content"), query.getKeyword())) {
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());
            } else {
                if (ObjUtil.isNull(query.getStartIndex()) || ObjUtil.isNull(query.getEndIndex())) {
                    Long count = stringRedisTemplate.opsForZSet().zCard(key);
                    query.setEndIndex(count);
                    if (count > 50L) {
                        query.setStartIndex(count - 50L);
                    } else {
                        query.setStartIndex(0L);
                    }
                }
                Set<String> set = stringRedisTemplate.opsForZSet().range(key, query.getStartIndex(), query.getEndIndex());
                return ListUtil.toList(set);
            }
        }
        query.setStartIndex(0L);
        return new ArrayList<>();

    }

    private ChatVO toVO(ImChat imChat) {
        ChatVO chatVO = BeanUtil.toBean(imChat, ChatVO.class);
        // 获取用户在线离线状态
        if (ObjUtil.equal(imChat.getType(), 1)) {
            ImUser imUser = imUserMapper.selectById(chatVO.getTargetId());
            chatVO.setStatus(imUser.getStatus());
            chatVO.setNickname(imUser.getNickname());
            chatVO.setAvatar(imUser.getAvatar());
        } else if (ObjUtil.equal(imChat.getType(), 2)) {
            ImGroup imGroup = groupMapper.selectById(chatVO.getTargetId());
            chatVO.setStatus("all");
            chatVO.setNickname(imGroup.getGroupName());
            chatVO.setAvatar(imGroup.getAvatar());
        }

        return chatVO;
    }


    public List<Long> getChatKeep(Long userId) {
        List<ImChannelContext> list = JimServerAPI.getByUserId(String.valueOf(userId));
        return list.stream()
                .filter(imChannelContext -> {
                    if (ObjUtil.isNotNull(imChannelContext.get("chatId"))) {
                        return true;
                    }
                    return false;
                })
                .map(imChannelContext -> {
                    Object chatId = imChannelContext.get("chatId");
                    return Long.parseLong(String.valueOf(chatId));
                }).distinct().collect(Collectors.toList());
    }
}
